<?php

/**
 * 表单元素
 */
abstract class FormElement
{
    protected $attr = array();
    protected $html = '';

    public function __construct($attr)
    {
        $this->attr = $attr;
    }

    public function make()
    {
        $str = ' ';
        foreach($this->attr as $key=>$val){
            if($key{0}=="_")//以下划线开始的键名,将不作为表单元素的属性
                continue;
            $val = str_replace("'", '"', $val);
            $str .= "$key='$val' ";
        }

        return $str;
    }

    /**
     * 获得表单元素的属性的值
     * @return mixed
     */
    public function __get($key)
    {
        return $this->attr[$key];
    }

    /**
     * 设置表单元素属性的值
     * @param string $var
     * @param string $val
     */
    public function __set($var, $val)
    {
		$this->attr[$var] = $val;
    }

    /**
     * 获得表得对象的 Html 代码
     * @return string
     */
    public function getHtml()
    {
		$this->make();
        return $this->html;
    }

    /**
     * 获得表得对象的 Html 代码
     * @return string
     */
    public function __toString()
    {
        return $this->getHtml();
    }
}


/**
 * 多行文本区域
 */
class FormElementTextarea extends FormElement
{
    public function make()
    {
        $attr = $this->attr;
        unset($attr['value'], $attr['type']);
        $str = ' ';
        foreach($attr as $key=>$val){
            if($key{0}=="_")//以下划线开始的键名,将不作为表单元素的属性
                continue;
            $val = str_replace("'", '"', $val);
            $str .= "$key='$val' ";
        }

        $val = $this->attr['value'];
        $str = str_replace(" value='{$this->attr['value']}'", "", $str);
        return $this->html = "<textarea{$str}>{$val}</textarea>\r\n";
    }
}


/**
 * 下拉菜单
 */
class FormElementSelect extends FormElement
{
    public function make()
    {
        $this->html = "";

        empty($this->attr['options']) && $this->attr['options'] = array(""=>"");
        $attr = $this->attr;

        foreach($this->attr['options'] as $val => $capture){
            $val = str_replace("'", '"', $val);
            if($val!=$this->attr['value'])
                $this->html .= "<option value='$val'>$capture</option>\r\n";
            else
                $this->html .= "<option value='$val' selected='selected'>$capture</option>\r\n";
        }


        unset($attr['options'], $attr['value'], $attr['type']);
        $str = ' ';
        foreach($attr as $key=>$val){
            if($key{0}=="_")//以下划线开始的键名,将不作为表单元素的属性
                continue;
            $str .= "$key='$val' ";
        }

        $this->html = "<select{$str}>{$this->html}</select>\r\n";

        return $this->html;
    }
}

/**
 * <input> 系列表单元素
 */
abstract class  FormElementInput extends FormElement
{
    public function make()
    {
        $attr = parent::make();

        return $this->html = "<input{$attr}/>\r\n";
    }
}

/**
 * 单选框
 */
class  FormElementRadio extends FormElementInput
{

}

/**
 * 复选框
 */
class  FormElementCheckbox extends FormElementInput
{

}

/**
 * 提交按钮
 */
class FormElementSubmit extends FormElementInput
{

}

/**
 * 密码输入框
 */
class FormElementPassword extends FormElementInput
{
}

/**
 * 普通按钮
 */
class FormElementButton extends FormElementInput
{

}

/**
 * 重置按钮
 */
class FormElementReset extends FormElementInput
{

}

/**
 * 单行文本框
 */
class FormElementText extends FormElementInput
{

}

/**
 * 隐藏域
 */
class FormElementHidden extends FormElementInput
{

}

/**
 * 选框组
 */
abstract class  FormElementGroup
{
    protected $html = '';
    protected $attr = array();
    protected $elements = array();

    public function __construct($attr)
    {
        $this->attr = $attr;
    }

    abstract public function make();

    public function __set($key, $val)
    {
        $this->attr[$key] = $val;
    }

    public function __get($key)
    {
        return $this->attr[$key];
    }

    abstract public function getHtml();

    public function __toString()
    {
        return $this->getHtml();
    }
}

/**
 * 单选框组
 */
class FormElementRadioGroup extends FormElementGroup
{
    public function getHtml()
    {
        $this->make();
        foreach($this->elements as $radio){
            if($radio->value == $this->attr['value'])
                $radio->checked = 'checked';
            $this->html .= trim($radio) . $radio->capture . " ";
        }

        return $this->html;
    }

    public function make()
    {
        $attr = array("type" => "radio",  "name" => $this->attr['name']);
        foreach($this->attr['options'] as $value => $capture){
            $attr['value'] = $value;
            $attr['capture'] = $capture;
            $this->elements[] = new FormElementRadio($attr);
        }
    }
}

/**
 * 复选框组
 */
class FormElementCheckboxGroup extends FormElementGroup
{
    public function getHtml()
    {
        $this->make();
        empty($this->attr['value']) && $this->attr['value'] = array();
        foreach($this->elements as $checkbox){
            if(in_array($checkbox->value, $this->attr['value'])){
                $checkbox->checked = 'checked';
            }
            $this->html .= trim($checkbox) . $checkbox->capture . " ";
        }
        return $this->html;
    }

    public function make()
    {
        $attr = array("type" => "checkbox",  "name" => $this->attr['name']."[]");
        foreach($this->attr['options'] as $value => $capture){
            $attr['value'] = $value;
            $attr['capture'] = $capture;
            $this->elements[] = new FormElementCheckbox($attr);
        }
    }
}



/**
 * 过滤规则工厂类
 */
class DataValidatorFactory
{
    static public function createValidator($validator)
    {
        if(strpos($validator, "MaxLength")!==false){
            list($validator, $maxLen) = explode("=", $validator);
            $validator = new MaxLengthValidator();
            $validator->maxLength = $maxLen;
            return $validator;
        }elseif(strpos($validator, "MinLength")!==false){
            list($validator, $minLen) = explode("=", $validator);
            $validator = new MinLengthValidator();
            $validator->minLength = $minLen;
            return $validator;
        }elseif(is_array($validator)){

            list($func, $msg) = each($validator);
            $validator = new MyValidator($func, $msg);
            return $validator;
        }

        $validator = trim($validator);
        $validator = ucfirst($validator)."Validator";

        if(!class_exists($validator)){
            throw new DebugException("没有此过滤规则: $validator");
        }

        return new $validator();
    }
}

abstract class DataValidator
{

    protected $msg = '你输入的数据不符合要求';
    protected $pattern;

    public function __construct($msg='')
    {
        if(!empty($msg))
            $this->msg = $msg;
    }

    public function check(&$val)
    {
        return preg_match($this->pattern, $val);
    }

    public function __get($var)
    {
        if($var == "msg")
            return $this->msg;
        else
            return null;
    }

    public function __set($var, $val)
    {
        throw new DebugException(get_class($this)."->$var 为只读属性");
    }
}

class EmailValidator extends DataValidator
{
    protected $msg = "你输入的是无效Email";
    protected $pattern = "/^[0-9a-zA-Z_\-]+@([0-9a-zA-Z\-]+)(\.[0-9a-zA-Z]+)+$/";
}

class MaxLengthValidator extends DataValidator
{
    public $maxLength = -1;

    public function check(&$val)
    {
        $this->msg = "最多只能输入{$this->maxLength}个字";
        return ($this->maxLength > -1 && strlen($val) < $this->maxLength);
    }
}

class MinLengthValidator extends DataValidator
{
    public $minLength = -1;

    public function check(&$val)
    {
        $this->msg = "最少要输入{$this->minLength}个字";
        return ($this->minLength > -1 && $this->minLength < strlen($val));
    }
}

class NotNullValidator extends DataValidator
{
    protected $msg = "输入不能为空";

    public function check(&$val)
    {
        return !empty($val);
    }
}

class MyValidator extends DataValidator
{
    protected $func = '';

    public function __construct($func, $msg)
    {
        $this->func = $func;
        parent::__construct($msg);
    }

    public function check($val)
    {
        $this->func = str_replace("{:value}", "\$val", $this->func);
        return !eval($this->func .";");
    }
}

class DebugException extends Exception {
    public function __construct($msg, $code=null)
    {
        parent::__construct($msg, $code);
    }
}

class UserException extends Exception {
}

class DataValidatorException extends UserException
{
    public function __construct($msg, $code=null)
    {
        parent::__construct($msg, $code);
    }
}

class  EmailValidatorException extends DataValidatorException
{
}


abstract class DataFilter
{
    static public function call($filter, &$data)
    {
        foreach($filter as $key => $f){
            $f = trim($f);
            $f = str_replace("{:value}", "\$data", $f);
            if(!is_int($key)){
                eval($f . ";");
            }else{
                if(strpos($f, "(")===false)
                    $data = $f($data);
                else
                    $data = eval("return $f;");
            }
        }
        return $data;
    }
}




/**
 * 表单生成类
 */
class FormMaker
{
    protected $attr = array();
    protected $html = '';
    protected $fieldsSettings = array();
    protected $elements = array();
    protected $tpl = array(
        'container' => "<div><h3>{:form->capture}</h3><form {:form->attr}><table>{:form->element}</table></form></div>",
        'item' => "<tr><td align='right'>{:elem->capture}</td><td>{:elem->body}</td><!--<td>{:elem->teller}</td>--></tr>\r\n"
    );


    public function __construct($attr, $fieldsSettings)
    {
        $this->attr = $attr['attr'];
        isset($attr['td']) && $this->td = $attr['td'];
        $this->capture = $attr['capture'];

        foreach($fieldsSettings as $table => $fields){
            foreach($fields as $field => $attr){
                if(strpos($field, ":")!==false){
                    list($attr['name'], $attr['capture']) = explode(":", $field);
                }else{
                    $attr['name'] = $attr['capture'] = $field;
                }
                $fieldName = $table. "[" . $attr["name"] . "]";
                $fieldsSettings[$table][$attr['name']] = $attr;
                $fieldsSettings[$table][$attr['name']]['name'] = $fieldName;
                $fieldsSettings[$table][$field] = null;
                unset($fieldsSettings[$table][$field]);
            }
        }

        $this->fieldsSettings = $fieldsSettings;
		$this->make();
    }

    /**
     * 生成一个表单
     * @return string
     */
    public function make()
    {
        $a = '';
        if(is_array($this->attr)){
            foreach($this->attr as $key=>$val){
                $a .= "$key='$val' ";
            }
            $this->attr = trim($a);
        }

        $this->html = str_replace(array("{:form->attr}", "{:form->capture}"),
                                  array($this->attr, $this->capture),
                                  $this->tpl['container']);

        foreach($this->fieldsSettings as $table => $fields){
            foreach($fields as $field => $attr){
                $this->addElement($table, $field, $attr);
            }
        }

        return $this;
    }

    /**
     * 生成表单元素
     * @param string $name 元素名称, 如果$name中有冒号,则冒号之前的字符是元素name属性,冒号之后的字符是提示标题
     * @param array $attr 元素属性
     * @return string
     */
    public function addElement($table, $field, $attr)
    {
        empty($attr['type']) && $attr['type'] = 'text';
        $class= "FormElement".ucfirst(strtolower($attr['type']));
        $element = new $class($attr);
        return $this->elements[$table][$field] = $element; //把表单元素对象保存起来
    }

    /**
     * @return string
     */
    public function getHtml()
    {
        $r = false;
        if($this->td == 3){
            $this->tpl['item'] = str_replace(array("<!--", "-->"), "", $this->tpl['item']);
            $r = true;
        }
        $this->addSubmitButton();
        foreach($this->elements as $table => $arr){
            foreach($arr as $name => $elem){
                $html = str_replace(array("{:elem->capture}", "{:elem->body}"), array($elem->capture, $elem), $this->tpl['item']);
                if($r){
                    if($elem->teller===null)
                        $elem->teller = '&nbsp;';
                    $html = str_replace('{:elem->teller}', $elem->teller, $html);
                }
                $this->html = str_replace('{:form->element}', $html.'{:form->element}', $this->html);
            }
        }
        return str_replace("{:form->element}", "", $this->html);
    }

    /**
     * 接收数据,将数据填充到对应的表单域里,如果表单不存在某个域,则该数据将被丢弃
     * @param array $data
     */
	public function receiveSubmit(&$data)
	{
        if(empty($data))
            return false;

        foreach($this->elements as $table => $elements){
            foreach($elements as $name => $elem){
                if(isset($data[$table][$name]))
                    $this->elements[$table][$name]->value = $data[$table][$name];
                else
                    $this->elements[$table][$name]->value = null;
            }
        }
	}

    /**
     * 检查提交的数据是否符合过滤规则
     */
    public function checkSubmit(&$data)
    {
        if(empty($data))
            return ;
        $validator = null;
        $result = true;
        foreach($this->elements as $table => $elements){
            foreach($elements as $field => $elem){
                if(!empty($this->fieldsSettings[$table][$field]['_validator'])){
                    $validators = $this->fieldsSettings[$table][$field]['_validator'];
                    !is_array($validators) && $validators = array($validators);
                    for($i=sizeof($validators)-1; $i>=0; $i--){
                        //对数据进行检查
                        $validator = DataValidatorFactory::createValidator($validators[$i]);
                        if(!$validator->check($data[$table][$field])){//数据不符合规则
                            $this->elements[$table][$field]->teller = $validator->msg;
                            $result = false;
                        }
                    }

                    if(!empty($this->fieldsSettings[$table][$field]['_filter'])){
                        //对数据进行过滤
                        DataFilter::call($this->fieldsSettings[$table][$field]['_filter'], $data[$table][$field]);
                    }
                }
            }
        }

        return $result;
    }

    public function __get($key)
    {
        return $this->elements[$table][$key]->value;
    }

    /**
     * 添加一个提交按钮
     * @param string $val 提交按钮的值
     */
    public function addSubmitButton($val=null)
    {
        empty($val) && $val = '提交';
        $this->addElement('__#$#', 'submitButton: ', array('type' => 'submit', 'value' => $val));
    }

    public function __toString()
    {
        return $this->getHtml();
    }
}

/**
 * 表单自动处理类
 */
class AutoForm extends FormMaker
{
    protected $db;

    public function __construct($attr, $fieldsSettings)
    {
        if(is_file($fieldsSettings)){
            $fieldsSettings = include_once($fieldsSettings);
        }
        parent::__construct($attr, $fieldsSettings);
    }

    public function setDb($db)
    {
        $this->db = $db;
    }

    /**
     * 保存数据, 不在 $this->fieldsSettings 定义里的数据将被忽略
     * @param array $p 包含待保存数据的数据
     * @return bool
     */
    public function saveSubmit(&$p)
    {
        if(empty($p))
            return $this;
        $r = true;

        foreach($this->fieldsSettings as $table => $cols){
            $data = array();
            foreach($cols as $col => $attr){
                $col = substr($col, 0, strpos($col, ":"));
                $data[$col] = $p[$table][$col];
            }

            $r = $r && $this->db->save($table, $data);
        }

        return $r;
    }


}
